#include "fdfs_api.h"
#include "fcgi_config.h"
#include "fcgi_stdio.h"
#include "mysql_conn.h"
#include <string.h>
// #include "sqlconnpool_C.h"
#include "make_log.h"
#include <unistd.h>
#include "cfg.h"
#include "utils.h"
#include "mysql_conn.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>

#include "fdfs_client.h" //这个

#define BUFFER_SIZE 2048
#define CONF "/etc/fdfs/client.conf"

static char mysql_user[128] = {0};
static char mysql_pwd[128] = {0};
static char mysql_db[128] = {0};


int upload_to_dstorage(char* filename, char* fileid);
int upload_file(const char *confFile, const char *myFile, char *fileID);

int Init(int* content_length, char** boundary);
int RemoveFile(const char* fname);
int recv_files(int content_length, char* user, char* filename, char* md5, long* size, char* boundary);
int store_fileinfo_to_mysql(char* user, char* filename, char* md5, long size, char* fileid, char* fdfs_file_url);
int make_file_url(char* fileid, char* fdfs_file_url);

int main ()
{
    char fileid[1024] = {0};
    char user[64] = {0};
    char filename[100] = {0};
    char md5[64] = {0};
    long size = 0;
    char* boundary = NULL;
    char fdfs_file_url[512] = {0};

    int content_length = 0;
    int ret = 0;

    // ConnPool_Init("localhost", 3306, "chahu", "123456", "storage", 3);

    read_cfg(mysql_user, mysql_pwd, mysql_db);

    while (FCGI_Accept() >= 0)
    {
        LOG("upload");
        Init(&content_length, &boundary);
        memset(filename, '\0', 100);
        memset(fileid, '\0', 1024);
        memset(user, '\0', 64);
        memset(md5, '\0', 64);
        memset(fdfs_file_url, '\0', 512);
        size = 0;

        LOG("content_length = %d\n", content_length);
        LOG("boundary = %s\n", boundary);
        ret = 0;

        printf("Content-type: text/html; charset=utf-8\r\n\r\n");
        
        do
        {
            if(recv_files(content_length, user, filename, md5, &size, boundary) < 0)
            {
                LOG("recv_files error\n");
                ret = -1;
                break;
            }

            // if(upload_to_dstorage(filename, fileid) < 0)
            // {
            //     LOG("upload_to_dstorage() error\n");
            //     RemoveFile(filename);
            //     ret = -1;
            //     break;
            // }

            if(upload_file(CONF, filename, fileid) < 0)
            {
                LOG("upload_file() error\n");
                RemoveFile(filename);
                ret = -1;
                break;
            }

            LOG("已存入storage");

            RemoveFile(filename);

            LOG("上传成功！");
            
            // MYSQL* mysql = GetConn();

            if(make_file_url(fileid, fdfs_file_url) < 0)
            {
                ret = -1;
                LOG("make_file_url() error\n");
                break;
            }

            if(store_fileinfo_to_mysql(user, filename, md5, size, fileid, fdfs_file_url) < 0)
            {
                ret = -1;
                LOG("store_fileinfo_to_mysql() error\n");
                break;
            }

        }while(0);

        char* out = NULL;

        if(boundary != NULL)
        {
            free(boundary);
            boundary = NULL;
        }
        //给前端返回，上传情况
        /*
        上传文件：
        成功：{"code":"008"}
        失败：{"code":"009"}
        */

        if(ret == 0)
        {
            out = return_status("008");
        }
        else
        {
            out = return_status("009");
        }

        if(out != NULL)
        {
            printf(out);
            free(out);
        }
    }
    return 0;
}

int Init(int* content_length, char** boundary)
{
    int ret = 0;
    char* content_type = getenv("CONTENT_TYPE");
    if(content_type != NULL)
    {
        char* tmp = strstr(content_type, "boundary");
        tmp += strlen("boundary=");
        char* result = malloc(strlen(tmp) + 1);
        strcpy(result, tmp);
        *boundary = result;
    }

    char *contentLength = getenv("CONTENT_LENGTH");
    if(contentLength != NULL)
    {
        *content_length = strtol(contentLength, NULL, 10);
    }
    else
    {
        *content_length = 0;
    }

    if(*content_length <= 0)
    {
        LOG("content_length error!");
        ret = -1;
    }
    return ret;
}

int upload_to_dstorage(char* filename, char* fileid)
{
    int ret = 0;
    pid_t pid = 0;
    int fd[2] = {0};

    // do
    // {
        if(pipe(fd) < 0)
        {
            LOG("pipe() error!\n");
            ret = -1;
            // break;
            goto END;
        }

        pid = fork();
        if(pid < 0)
        {
            LOG("fork error\n");
            ret = -1;
            // break;
            goto END;
        }

        if(pid == 0)
        {
            //关闭读端
            close(fd[0]);
            
            dup2(fd[1], STDOUT_FILENO);
            dup2(fd[1], STDERR_FILENO);

            // char fdfs_cli_conf_path[256] = {0};
            // get_cfg_value(CFG_PATH, "dfs_path", "client", fdfs_cli_conf_path);

            execlp("fdfs_upload_file", "fdfs_upload_file", CONF, filename, NULL);
            
            LOG("execlp fdfs_upload_file error\n");
            
            //execlp("/bin/ls", "ls", "-l", NULL);
            
            exit(0);
        }
        else
        {
            close(fd[1]);

            int n = read(fd[0], fileid, 1024);
            LOG("fileid = %s, %d\n", fileid, n);

            trim_space(fileid);
            LOG("fileid = %s\n", fileid);

            if(strlen(fileid) == 0)
            {
                LOG("upload failed! \n");
                ret = -1;
                // break;
                goto END;
            }

            LOG("get [%s] success!\n", fileid);

            close(fd[0]);
        }
        
    // }while(0);

END:
    wait(NULL);
    return ret;
}

//使用fastDFS API实现
int upload_file(const char *confFile, const char *myFile, char *fileID) 
{
    char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
    ConnectionInfo *pTrackerServer;
    int result;
    int store_path_index;
    ConnectionInfo storageServer;

    //TODO fdfs_client_init解析传入的配置文件
    if ((result = fdfs_client_init(confFile)) != 0) {
        return result;
    }
    //TODO tracker_get_connection在上面client配置文件里面
    //TODO 有追踪器的ip:port，所以这里就是连接追踪器，初始化pTrackerServer指针
    pTrackerServer = tracker_get_connection();
    if (pTrackerServer == NULL) {
        fdfs_client_destroy();
        return errno != 0 ? errno : ECONNREFUSED;
    }

    *group_name = '\0';

    //TODO tracker_query_storage_store追踪器 查询 存储节点 的 存储
    //TODO storageServer是传入参数，对storageServer赋值
    //TODO group_name是一个char数组，对group_name赋值
    //TODO store_path_index是传入参数，对store_path_index赋值
    if ((result = tracker_query_storage_store(pTrackerServer, \
                    &storageServer, group_name, &store_path_index)) != 0) {
        fdfs_client_destroy();
        LOG("tracker_query_storage fail, " \
            "error no: %d, error info: %s\n", \
            result, STRERROR(result));
        return result;
    }
    //TODO storage_upload_by_filename1查到了存储节点，就进行上传的动作
    //TODO file_id传出参数，如果上传成功了，就打印file_id
    result = storage_upload_by_filename1(pTrackerServer, \
            &storageServer, store_path_index, \
            myFile, NULL, \
            NULL, 0, group_name, fileID);
    if (result == 0) {
        LOG("fileID = %s\n", fileID);
    }
    else {
        LOG("upload file fail, " \
            "error no: %d, error info: %s\n", \
            result, STRERROR(result));
    }
    //TODO tracker_close_connection_ex与追踪器断开连接
    tracker_close_connection_ex(pTrackerServer, true);
    fdfs_client_destroy();

    return result;
}

int recv_files(int content_length, char* user, char* filename, char* md5, long* size, char* boundary)
{
    char buffer[BUFFER_SIZE] = {0};
    memset(buffer, '\0', BUFFER_SIZE);

    char* start = buffer;
    char* pch_start = NULL;
    int content_num = 0;   

    char* pch = NULL;
    FILE* fp = NULL;
    content_num = fread(buffer, 1, BUFFER_SIZE, stdin);
    LOG("buffer = %s\n", buffer);
    long file_size = 0;

    pch = strstr(buffer, boundary);
    if(pch != NULL)
    {
        pch += strlen(boundary);
    }

    if((pch_start = strstr(pch, "user=\"")) != NULL)
    {
        pch_start += strlen("user=\"");
        char* user_start = pch_start;
        char* user_end = strchr(pch_start, '\"');
        unsigned len = user_end - user_start;

        memcpy(user, user_start, len);
        LOG("user = %s\n", user);
    }

    if((pch_start = strstr(pch, "filename=\"")) != NULL)
    {
        pch_start += strlen("filename=\"");
        char* filename_start = pch_start;

        char* filename_end = strchr(pch_start, '\"');
        unsigned len = filename_end - filename_start;

        memcpy(filename, filename_start, len);
        LOG("filename = %s\n", filename);
        fp = fopen(filename, "wb");
        if(fp == NULL)
        {
            LOG("Failed to create file");
            return -1;
        }

        // printf("<h3>filename: %s</h3>\n", filename);
    }
    if((pch_start = strstr(pch, "md5=\"")) != NULL)
    {
        pch_start += strlen("md5=\"");
        char* md5_start = pch_start;
        char* md5_end = strchr(pch_start, '\"');
        unsigned len = md5_end - md5_start;
        memcpy(md5, md5_start, len);
        LOG("md5 = %s\n", md5);
    }
    
    if((pch_start = strstr(pch, "size=")) != NULL)
    {
        pch_start += strlen("size=");
        char* size_start = pch_start;
        char* size_end = strchr(pch_start, '\r');
        unsigned len = size_end - size_start;
        char tmp[24] = {0};
        memcpy(tmp, size_start, len);

        *size = atol(tmp);
        LOG("size = %ld\n", *size);
        file_size = *size;
    }
    if((pch = strstr(pch, "\r\n\r\n")) != NULL)
    {
        pch += strlen("\r\n\r\n");
        /** 
        content_num -= (pch - start);
        if(strstr(pch, boundary) != NULL)
        {
            content_num -= (strlen(boundary)+4);
        }

        content_length -= (pch - start);
        content_length -= (strlen(boundary)+6);
        while(content_length > 0)
        {
            content_length -= content_num;
            fwrite(pch, 1, content_num, fp);
            content_num = fread(buffer, 1, content_length > BUFFER_SIZE ? BUFFER_SIZE : content_length, stdin);
            pch = buffer;
        }
        */

        content_num -= (pch - start);

        while(file_size > 0)
        {
            content_num = content_num > file_size ? file_size : content_num;
            fwrite(pch, 1, content_num, fp);
            file_size -= content_num;
            content_num = fread(buffer, 1, file_size > BUFFER_SIZE ? BUFFER_SIZE : file_size, stdin);
            pch = buffer;
        }

        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int RemoveFile(const char* fname)
{
    int ret = remove(fname);

    if (ret == -1) 
    {
        // printf("Failed to delete the file.\n");
        LOG("Failed to deleted thre file\n");
        return -1;
    }
      
    LOG("The file has been deleted.\n");
      
    return 0;
}

int store_fileinfo_to_mysql(char* user, char* filename, char* md5, long size, char* fileid, char* fdfs_file_url)
{
    int ret = 0;
    MYSQL* mysql = NULL;

    time_t now;
    char create_time[25] = {0};
    char suffix[8] = {0};
    char sql_cmd[512] = {0};
    LOG("store_fileinfo_to_mysql()\n");
    do
    {
        mysql = get_mysql(mysql_user, mysql_pwd, mysql_db);
        LOG("get_mysql()\n");
        if(mysql == NULL)
        {
            LOG("get_mysql error~\n");
            ret = -1;
            break;
        }

        //设置数据库编码
        mysql_query(mysql, "set names utf8mb4");

        //获取文件后缀
        get_file_suffix(filename, suffix);
LOG("suffix: %s\n", suffix);
        sprintf(sql_cmd, "insert into file_info(md5, file_id, url, size, type, count) values('%s', '%s', '%s', %ld, '%s', %d)",
        md5, fileid, fdfs_file_url, size, suffix, 1);

        if(mysql_query(mysql, sql_cmd) != 0)
        {
            LOG("%s 插入失败：%s\n", sql_cmd, mysql_error(mysql));
            ret = -1;
            break;
        }

        LOG("%s 文件信息插入成功\n", sql_cmd);

        now = time(NULL);
        strftime(create_time, sizeof(create_time)-1, "%Y-%m-%d %H:%M:%S", localtime(&now));

        /*
        -- =============================================== 用户文件列表
        -- user 文件所属用户
        -- md5 文件md5
        -- create_time 文件创建时间
        -- file_name 文件名字
        -- shared_status 共享状态, 0为没有共享， 1为共享
        -- pv 文件下载量，默认值为0，下载一次加1
        */
        //sql语句

        sprintf(sql_cmd, "insert into user_file_list(user, md5, create_time, file_name, shared_status, pv) values('%s', '%s', '%s', '%s', %d, %d)", user, md5, create_time, filename, 0, 0);
        if(mysql_query(mysql, sql_cmd) != 0)
        {
            LOG("%s 操作失败：%s\n", sql_cmd, mysql_error(mysql));
            ret = -1;
            break;
        }

        sprintf(sql_cmd, "select count from user_file_count where user = '%s'", user);
        int ret2 = 0;
        char tmp[512] = {0};
        int count = 0;
        ret2 = process_result_one(mysql, sql_cmd, tmp);
        if(ret2 == 1)
        {
            sprintf(sql_cmd, "insert into user_file_count(user, count) values('%s', %d)", user, 1);
        }
        else if(ret2 == 0)
        {
            count = atoi(tmp);
            sprintf(sql_cmd, "update user_file_count set count = %d where user = '%s'", count+1, user);
        }
        
        if(mysql_query(mysql, sql_cmd) != 0)
        {
            LOG("%s操作失败：%s\n", sql_cmd, mysql_error(mysql));
            ret = -1;
            break;
        }

        // mysql = get_mysql(mysql_user, mysql_pwd, "storage");
        // int ret = store_data(mysql, filename, fileid);
        // if(ret != 0)
        // {
        //     LOG("store_data error~: %s\n", mysql_error(mysql));
        //     // FreeConn(mysql);
        //     ret = -1;
        //     break;
        // }

        // FreeConn(mysql);
        // mysql_close(mysql);
        LOG("mysql插入成功！\n");
    }while(0);

    if(mysql != NULL)
    {
        mysql_close(mysql);
    }

    return ret;
}

int make_file_url(char* fileid, char* fdfs_file_url)
{
    LOG("mak_file_url() 调用\n");
    int ret = 0;
    
    char* p = NULL;
    char* q = NULL;
    char* k = NULL;

    char fdfs_file_stat_buf[512] = {0};
    char fdfs_file_host_name[32] = {0};

    pid_t pid;
    int fd[2];

    do
    {
        if(pipe(fd) < 0)
        {
            LOG("pipe() error\n");
            ret = -1;
            break;
        }

        pid = fork();
        if(pid < 0)
        {
            LOG("fork() error\n");
            ret = -1;
            break;
        }

        if(pid == 0)
        {
            close(fd[0]);

            dup2(fd[1], STDOUT_FILENO);
            dup2(fd[1], STDERR_FILENO);

            char fdfs_cli_conf_path[256] = {0};
            get_cfg_value(CFG_PATH, "dfs_path", "client", fdfs_cli_conf_path);

            LOG("fdfs_cli_conf_path: %s\n", fdfs_cli_conf_path);
            
            execlp("fdfs_file_info", "fdfs_file_info", fdfs_cli_conf_path, fileid, NULL);

            LOG("execlp(fdfs_file_info) error\n");

            close(fd[1]);
            // exit(0);
        }
        else
        {
            close(fd[1]);

            read(fd[0], fdfs_file_stat_buf, 512);

            LOG("fdfs_file_info %s\n", fdfs_file_stat_buf);

            wait(NULL);
            close(fd[0]);

            p = strstr(fdfs_file_stat_buf, "source ip address: ");
            q = p + strlen("source ip address: ");
            k = strchr(q, '\n');

            strncpy(fdfs_file_host_name, q, k - q);
            fdfs_file_host_name[k-q] = '\0';

            char storage_web_server_port[20] = {0};
            get_cfg_value(CFG_PATH, "storage_web_server", "port", storage_web_server_port);
            strcat(fdfs_file_url, "http://");
            // strcat(fdfs_file_url, fdfs_file_host_name);

            strcat(fdfs_file_url, "43.139.182.126");

            strcat(fdfs_file_url, ":");
            strcat(fdfs_file_url, storage_web_server_port);
            strcat(fdfs_file_url, "/");
            strcat(fdfs_file_url, fileid);
            LOG("file url is: %s\n", fdfs_file_url);
        }
    }while(0);
    return ret;
}